#include <stdbool.h>
#include <string.h>
#include "fifo.h"

#define __fifo_mini(a,b) ((a) < (b) ? (a) : (b))

/******************************************************************************
 * @brief 返回 fifo 未使用空间字节大小
 * @param fifo fifo结构体指针
 * @return uint32_t 未使用空间字节大小
******************************************************************************/
static inline uint32_t __fifoUnused(__S_FIFO *fifo)
{
    return fifo->size - (fifo->in - fifo->out);
}

/******************************************************************************
 * @brief 返回 fifo 有效数据字节大小
 * @param fifo fifo 结构体
 * @return uint32_t 有效数据字节大小
******************************************************************************/
static inline uint32_t __fifoUsed(__S_FIFO *fifo)
{
    return fifo->in - fifo->out;
}

/******************************************************************************
 * @brief 返回 buffer 偏移量
 * @param fifo fifo结构体指针
 * @param offset 当前偏移量
 * @return uint32_t 偏移字节大小
******************************************************************************/
static inline uint32_t __fifoGetOffset(__S_FIFO *fifo,uint32_t offset)
{
    return (offset & (fifo->size - 1));
}

/******************************************************************************
 * @brief 返回 fifo 是否已满
 * @param fifo fifo结构体指针
 * @return true fifo 已满
 * @return false fifo 未满
******************************************************************************/
static inline bool __fifoIsFull(__S_FIFO *fifo)
{
    return __fifoUsed(fifo) == fifo->size;
}

/******************************************************************************
 * @brief 获取 fifo 是否为空
 * @param fifo fifo 结构体
 * @return true fifo 为空
 * @return false fifo 不为空
******************************************************************************/
static inline bool __fifoIsEmpty(__S_FIFO *fifo)
{
    return fifo->in == fifo->out;
}

/******************************************************************************
 * @brief 初使化 fifo 结构体
 * @param fifo fifo结构体指针
 * @param bufferAddr buffer指针
 * @param size buffer字节大小
 * @return appStatus_t 返回初使化结果
******************************************************************************/
int fifoInit(__S_FIFO *fifo,void *bufferAddr,uint32_t size)
{
    //为了优化速度，size必需为2的幂
    if( (size > 0) && ((size & (size - 1)) > 0) )
    {
        return -1;
    }
    fifo->in = 0 ;
    fifo->out = 0 ;
    fifo->size = size;
    fifo->buffer = bufferAddr;
    return 0;
}

/******************************************************************************
 * @brief 清零fifo
 * @param fifo fifo结构体指针
 * @return int 返回值
******************************************************************************/
int fifoClear(__S_FIFO *fifo)
{
    fifo->in = 0 ;
    fifo->out = 0 ;
    return 0;
}

/******************************************************************************
 * @brief 往 fifo 中写入数据
 * @param fifo fifo结构体指针
 * @param buffer 数据指针
 * @param size 数据字节大小
 * @return uint32_t 返回已输入到 fifo 中的数据字节大小
******************************************************************************/
uint32_t fifoWrite(__S_FIFO *fifo,const void *buffer,uint32_t size)
{
    uint32_t l;
    uint32_t offset;
    uint32_t unused ;

    if(__fifoIsFull(fifo) == true)
    {
        return 0;
    }

    unused = __fifoUnused(fifo);
    size = __fifo_mini(size,unused);//要写入的大小与可用空间相比，取最小值
    offset = __fifoGetOffset(fifo,fifo->in);//获取写入位置
    l = __fifo_mini(size,fifo->size - offset);//未尾空间与要写入的大小相比，取最小值
    memcpy((char *)fifo->buffer + offset,buffer,l);
    memcpy(fifo->buffer,(char *)buffer + l,size - l);
    fifo->in += size;

    return size;
}

/******************************************************************************
 * @brief 从 fifo 中读取数据
 * @param fifo fifo结构体指针
 * @param buffer 接收数据的指针
 * @param size 需要接收的数据大小
 * @return uint32_t 返回成功接收的数据大小
******************************************************************************/
uint32_t fifoRead(__S_FIFO *fifo,void *buffer,uint32_t size)
{
    uint32_t l;
    uint32_t offset;
    uint32_t used ;

    if(__fifoIsEmpty(fifo) == true)
    {
        return 0;
    }

    used = __fifoUsed(fifo);
    size = __fifo_mini(size,used);
    offset = __fifoGetOffset(fifo,fifo->out);
    l = __fifo_mini(size,fifo->size - fifo->out);
    memcpy(buffer,(char *)fifo->buffer + offset,l);
    memcpy((char *)buffer + l,fifo->buffer,size - l);
    fifo->out += size;

    return size;
}

/******************************************************************************
 * @brief 获取fifo中可读取的数据
 * @param fifo fifo结构体指针
 * @return uint32_t 返回可读取字节大小
******************************************************************************/
uint32_t fifoGetReadableSpaceSize(__S_FIFO *fifo)
{
    return __fifoUsed(fifo);
}

/******************************************************************************
 * @brief 获取fifo中未使用的数据
 * @param fifo fifo结构体指针
 * @return uint32_t 返回未使用的字节大小
******************************************************************************/
uint32_t fifoGetWriteableSpaceSize(__S_FIFO *fifo)
{
    return __fifoUnused(fifo);
}
